package cn.eaay.stock.service.impl;

import cn.eaay.stock.common.constants.StockConstants;
import cn.eaay.stock.common.domain.StockInfoConfig;
import cn.eaay.stock.common.enums.ResponseCode;
import cn.eaay.stock.common.pull.PullRemoteStockApiDataService;
import cn.eaay.stock.dto.JsonResult;
import cn.eaay.stock.dto.StockRtInfoDto;
import cn.eaay.stock.mapper.StockRtInfoMapper;
import cn.eaay.stock.pojo.StockRtInfo;
import cn.eaay.stock.service.StockRtInfoService;
import cn.eaay.stock.utils.DateTimeUtil;
import cn.eaay.stock.utils.DateUtil;
import cn.eaay.stock.utils.IdWorker;
import cn.eaay.stock.vo.resp.R;
import cn.eaay.stock.vo.stock.StockBusinessVo;
import cn.eaay.stock.vo.stock.StockRtInfoVo;
import cn.eaay.stock.vo.stock.StockWeekRtInfoVo;
import cn.eaay.stock.vo.stock.StockWeekVo;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 个股详情服务
 */
@Slf4j
@Service
public class StockRtInfoServiceImpl implements StockRtInfoService {

    @Resource
    private StockInfoConfig stockInfoConfig;

    @Resource
    private PullRemoteStockApiDataService pullRemoteStockApiDataService;

    @Resource
    private StockRtInfoMapper stockRtInfoMapper;

    @Resource
    private IdWorker idWorker;

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    public void getStockRtInfo() {
        // 获取个股代码列表
        List<String> stockCodeList = stockInfoConfig.getCodeList();
        if (CollectionUtils.isEmpty(stockCodeList)) {
            log.info("股票代码为空 => {}", stockCodeList);
            throw new IllegalArgumentException("股票代码列表不能为空!");
        }
        Lists.partition(stockCodeList, 2).forEach(codeList -> {
            // 多线程执行
            threadPoolTaskExecutor.execute(() -> {
                List<StockRtInfo> stockRtInfos = Lists.newArrayList();
                for (String code : codeList) {
                    // 根据个股代码拉取远程数据
                    JsonResult<StockRtInfoDto> stockRtInfoData = pullRemoteStockApiDataService.pullApi(StockConstants.STOCK_RT_INFO_API_URL + "?code=" + code);
                    // 将拉取的数据序列化
                    List<StockRtInfoDto> stockRtInfoDto = gsonBuild(stockRtInfoData.getResult());
                    // 将数据转为想要的对象
                    transformObject(stockRtInfoDto, stockRtInfos);
                    log.info("数据转换完毕, size => {}", stockRtInfos.size());
                }
                if (CollectionUtils.isEmpty(stockRtInfos)) {
                    log.info("股票数据为空 => {}", stockRtInfos);
                    throw new IllegalArgumentException("股票数据为空!");
                }
                stockRtInfoMapper.insertBatch(stockRtInfos);
            });
        });
    }


    /**
     * 使用Gson构建对象
     *
     * @param stockRtInfoData
     * @return
     */
    private List<StockRtInfoDto> gsonBuild(List<StockRtInfoDto> stockRtInfoData) {
        // TODO: 校验参数 stockRtInfoData
        // 序列化为json
        Gson gson = new Gson();
        String stockRtInfoData2Json = gson.toJson(stockRtInfoData);
        // 反序列化为对象
        List<StockRtInfoDto> stockRtInfoDto = gson.fromJson(stockRtInfoData2Json,
                new TypeToken<List<StockRtInfoDto>>() {
                }.getType());
        if (CollectionUtils.isEmpty(stockRtInfoDto)) {
            log.info("股票数据获取失败了，请检查接口是否可用!");
            throw new IllegalArgumentException("数据获取失败，请稍后重试!");
        }
        return stockRtInfoDto;
    }

    private void transformObject(List<StockRtInfoDto> target, List<StockRtInfo> stockRtInfos) {
        target.forEach(dto -> {
            dto.getTrend().forEach(trend -> {
                String[] trendArr = trend.split(",");
                // 获取当前时间
                Date curTime = null;
                try {
                    curTime = DateUtil.formatFullDate(trendArr[0]);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                // 获取当前价格
                BigDecimal curPrice = new BigDecimal(trendArr[1]);
                // 获取最新价格
                BigDecimal updatePrice = new BigDecimal(dto.getPrice());
                // 获取成交量
                String tradeAccount = trendArr[3];
                // 获取成交额
                BigDecimal tradeVolume = new BigDecimal(trendArr[4]);
                StockRtInfo stockRtInfo = StockRtInfo.builder().id(String.valueOf(idWorker.nextId()))
                        .stockCode(dto.getCode())
                        .curTime(curTime)
                        .stockName(dto.getName())
                        .curPrice(curPrice)
                        .openPrice(dto.getOpenningprice())
                        .preClosePrice(dto.getLastclosingprice())
                        .tradeAmount(tradeAccount)
                        .tradeVolume(tradeVolume)
                        .maxPrice(updatePrice)
                        .minPrice(updatePrice)
                        .build();
                stockRtInfos.add(stockRtInfo);
            });
        });
    }

    /**
     * 根据股票编码模糊查询股票信息
     *
     * @param stockCode 股票代码
     * @return
     */
    @Override
    public R<List<Map>> searchStockInfo(String stockCode) {
        List<Map> data = stockRtInfoMapper.searchStockInfoByCode(stockCode);
        if (CollectionUtils.isEmpty(data)) {
            return R.error(ResponseCode.EMPTY_DATA.getMessage());
        }
        return R.ok(data);
    }

    /**
     * 周K线数据查询
     *
     * @param stockCode
     * @return
     */
    @Override
    public R<List<StockWeekRtInfoVo>> getStockWeekKLine(String stockCode) {
        // stockCode = "000014";
        String currentDate = DateTime.now().toString();
        // 查询本周数据
        List<StockWeekVo> weekData = stockRtInfoMapper.selectStockWeekInfo(stockCode, currentDate);
        // TODO: 校验数据 weekData
        String currentStockCode = weekData.get(0).getStockCode();
        // 转为Map结构，key:第几周，value:List<StockWeekVo>
        Map<String, List<StockWeekVo>> groupByData = weekData.stream().collect(Collectors.groupingBy(StockWeekVo::getWeek));
        // 用于存储结果数据
        List<StockWeekRtInfoVo> weekInfos = Lists.newArrayList();
        groupByData.forEach((key, value) -> {
            System.out.println(key + ":" + value);
            BigDecimal minPrice = value.stream().filter(o -> o.getMinPrice() != null)
                    .map(StockWeekVo::getMinPrice)
                    .min(BigDecimal::compareTo).get();
            System.out.println(key + " -> 最低价格:" + minPrice);
            // 获取一周内最高价格
            BigDecimal maxPrice = value.stream().filter(o -> o.getMaxPrice() != null)
                    .map(StockWeekVo::getMaxPrice)
                    .max(BigDecimal::compareTo).get();
            System.out.println(key + " -> 最高价格:" + maxPrice);
            // 获取一周内最大时间
            Date maxDate = value.stream().filter(o -> o.getCurTime() != null)
                    .map(StockWeekVo::getCurTime)
                    .max(Date::compareTo).get();
            System.out.println(key + " -> 最大时间:" + maxDate);

            // 获取周一开盘价
            BigDecimal mondayOpenPrice = new BigDecimal(0);
            DateTime monday = new DateTime(maxDate).withDayOfWeek(1);
            Date mondayDate = monday.toDate();
            String mondayFormatDate = DateUtil.getDateFormat(mondayDate, "yyyy-MM-dd");
            List<Map> mondayList = stockRtInfoMapper.selectOpenAndClosePriceByDate(currentStockCode, mondayFormatDate, "asc");
            if (CollectionUtils.isEmpty(mondayList)) {
                System.out.println(key + "没有周一数据");
            } else {
                mondayOpenPrice = (BigDecimal) mondayList.get(0).get("openPrice");
                System.out.println(key + "->" + mondayFormatDate + "周一开盘价:" + mondayOpenPrice);
            }

            // 获取周五收盘价
            BigDecimal fridayClosePrice = new BigDecimal(0);
            DateTime friday = new DateTime(maxDate).withDayOfWeek(5);
            Date fridayDate = friday.toDate();
            String fridayFormatDate = DateUtil.getDateFormat(fridayDate, "yyyy-MM-dd");
            List<Map> fridayList = stockRtInfoMapper.selectOpenAndClosePriceByDate(currentStockCode, fridayFormatDate, "desc");
            if (CollectionUtils.isEmpty(fridayList)) {
                System.out.println(key + "没有周五数据");
            } else {
                fridayClosePrice = (BigDecimal) fridayList.get(0).get("closePrice");
                System.out.println(key + "->" + fridayFormatDate + "周五收盘价:" + fridayClosePrice);
            }

            // 获取平均价
            List<BigDecimal> collectPrice = value.stream()
                    .filter(price -> price.getCurPrice() != null)
                    .map(StockWeekVo::getCurPrice)
                    .limit(5).collect(Collectors.toList());
            System.out.println("价格列表:" + collectPrice);
            BigDecimal avgPrice = collectPrice.stream().reduce(BigDecimal.ZERO, BigDecimal::add)
                    .divide(new BigDecimal(collectPrice.size()), 4, RoundingMode.HALF_UP);
            System.out.println(key + " -> 平均价:" + avgPrice);
            StockWeekRtInfoVo weekInfo = StockWeekRtInfoVo.builder()
                    .stock_code(currentStockCode)
                    .minPrice(minPrice)
                    .maxPrice(maxPrice)
                    .openPrice(mondayOpenPrice)
                    .closePrice(fridayClosePrice)
                    .mxTime(DateUtil.getDateFormat(maxDate))
                    .avgPrice(avgPrice)
                    .yearWeek(key)
                    .build();
            System.out.println(weekInfo);
            weekInfos.add(weekInfo);
        });

        // 按照yearWeek升序排序
        weekInfos.sort(Comparator.comparing(StockWeekRtInfoVo::getYearWeek));
        return R.ok(weekInfos);
    }

    /**
     * 计算一周内平均价
     */
    private void weekAvgPrice(List<StockRtInfo> weekData) {
        weekData.forEach(data -> data.getCurPrice());
    }

    private void getMinPrice(Map<String, List<StockWeekVo>> groupByData) {

    }

    /**
     * 获取个股分时行情
     *
     * @param stockCode
     * @return
     */
    @Override
    public R<Map> getMinuteStockInfo(String stockCode) {
        // TODO: 参数校验
        String curDateTimeStr = DateTimeUtil.getLastDateString4Stock(DateTime.now());
        curDateTimeStr = "20220106100100";
        Map info = stockRtInfoMapper.getStockInfoByDateTime(stockCode, curDateTimeStr);
        if (null == info) {
            return R.error(ResponseCode.EMPTY_DATA.getMessage());
        }
        return R.ok(info);
    }

    /**
     * 获取个股实时交易流水
     *
     * @return
     */
    @Override
    public R<List<Map>> getStockTradeInfo(String stockCode) {
        String curDateTimeStr = DateTimeUtil.getLastDateString4Stock(DateTime.now());
        curDateTimeStr = "20220106100100";
        List<Map> infos = stockRtInfoMapper.selectStockTradeInfo(stockCode, curDateTimeStr);
        if (CollectionUtils.isEmpty(infos)) {
            return R.error(ResponseCode.EMPTY_DATA.getMessage());
        }
        return R.ok(infos);
    }
}
